#!/usr/bin/env python3
# -*- coding: utf-8 -*-

r'''
codePKu Python helper
created by leetom.
'''

import sys
import ssl

def check_version():
    v = sys.version_info
    if v.major == 3 and v.minor >= 4:
        return True
    print('Your current python is %d.%d. Please use Python 3.4.' % (v.major, v.minor))
    return False

if not check_version():
    exit(1)

import os, io, json, subprocess, tempfile
from urllib import parse
from wsgiref.simple_server import make_server

EXEC = sys.executable
PORT = 26337
TEMP = tempfile.mkdtemp(suffix='_py', prefix='learn_python_')
INDEX = 0
crtFile = "https://www.codepku.com/misc/local.codepku/local.crt"
keyFile = "https://www.codepku.com/misc/local.codepku/local.key"
infoFile = "https://www.codepku.com/misc/local.codepku/python.txt"

def downloadCertFiles():
    import requests
    r = requests.get(crtFile)
    crtPath = os.path.join(TEMP, 'local.crt')
    keyPath = os.path.join(TEMP, 'local.key')
    with open (crtPath, 'wb') as f:
        f.write(r.content)
    print("save crt file to %s" % crtPath)
    r = requests.get(keyFile)
    with open(keyPath, 'wb') as f:
        f.write(r.content)
    print("save key file to %s" % keyPath)

    return (crtPath, keyPath)

def getHelperInfo():
    import requests
    r = requests.get(infoFile)
    r.encoding = 'utf-8'
    return r.text

def main():
    try:
        (cert, key) = downloadCertFiles()
        print(getHelperInfo())
    except Exception as err:
        print("获取证书失败，请确认已经连接到互联网" + str(err))
        return 0
    try:
        httpd = make_server('127.0.0.1', PORT, application)
        httpd.socket = ssl.wrap_socket(httpd.socket, keyfile=key, certfile=cert, server_side=True)
        print('编玩边学Python助手正在监听端口 %d \n 请到 https://www.codepku.com/ 进行编程...' % PORT)
        httpd.serve_forever()
    except Exception as err:
        print('Python助手启动失败，请关闭其他助手或任何占用 %d 端口的程序，或者重新启动电脑后尝试' % PORT + str(err))

def get_name():
    global INDEX
    INDEX = INDEX + 1
    return 'test_%d' % INDEX

def write_py(name, code):
    fpath = os.path.join(TEMP, '%s.py' % name)
    with open(fpath, 'w', encoding='utf-8') as f:
        f.write(code)
    print('Code wrote to: %s' % fpath)
    return fpath

def decode(s):
    try:
        return s.decode(sys.stdout.encoding)
    except UnicodeDecodeError:
        return s.decode('gbk')

def application(environ, start_response):
    host = environ.get('HTTP_HOST')
    method = environ.get('REQUEST_METHOD')
    path = environ.get('PATH_INFO')

    if method == 'GET' and path == '/':
        start_response('200 OK', [('Content-Type', 'text/html')])
        return [b'<html><head><title>Learning Python</title></head><body><form method="post" action="/run"><textarea name="code" style="width:90%;height: 600px"></textarea><p><button type="submit">Run</button></p></form></body></html>']
    if method == 'GET' and path == '/env':
        start_response('200 OK', [('Content-Type', 'text/html')])
        L = [b'<html><head><title>ENV</title></head><body>']
        for k, v in environ.items():
            p = '<p>%s = %s' % (k, str(v))
            L.append(p.encode('utf-8'))
        L.append(b'</html>')
        return L
    # 跨域调用，OPTIONS应该返回允许的域名
    if method == 'OPTIONS' and path == "/run":
        headers = [('Content-Type', 'application/json')]
        headers.append(('Access-Control-Allow-Origin', '*'))
        headers.append(('Access-Control-Allow-Headers', 'X-Requested-With, Content-type, X-CSRF-Token'))
        headers.append(('Access-Control-Allow-Methods', 'GET,POST,OPTIONS'))
        headers.append(('Allow', 'POST, HEAD, OPTIONS, GET'))
        start_response('200 OK', headers)
        return [b'1']   #随便返回一个值。主要是需要Headers
    # 请求是否合法
    if method != 'POST' or path != '/run' or not environ.get('CONTENT_TYPE', '').lower().startswith('application/x-www-form-urlencoded'):
        start_response('400 Bad Request', [('Content-Type', 'application/json')])
        return [b'{"error":"bad_request"}']

    #判断是否提交了代码 
    s = environ['wsgi.input'].read(int(environ['CONTENT_LENGTH']))
    qs = parse.parse_qs(s.decode('utf-8'))
    if not 'code' in qs:
        start_response('400 Bad Request', [('Content-Type', 'application/json')])
        return [b'{"error":"invalid_params"}']

    #校验完毕，开始保存并运行程序
    name = qs['name'][0] if 'name' in qs else get_name()
    code = qs['code'][0]
    headers = [('Content-Type', 'application/json')]
    origin = environ.get('HTTP_ORIGIN', '')
    headers.append(('Access-Control-Allow-Origin', origin))
    start_response('200 OK', headers)
    r = dict()
    try:
        fpath = write_py(name, code)
        print('Execute: %s %s' % (EXEC, fpath))
        # r['output'] = decode(subprocess.check_output([EXEC, fpath], stderr=subprocess.STDOUT, timeout=20))
        r['output'] = decode(subprocess.check_output([EXEC, fpath], stderr=subprocess.STDOUT))
        r['error'] = ''
    except subprocess.CalledProcessError as e:
        r = dict(error='Exception', output=decode(e.output))
    except subprocess.TimeoutExpired as e:
        r = dict(error='Timeout', output='执行超时')
    except subprocess.CalledProcessError as e:
        r = dict(error='Error', output='执行错误')
    print('Execute done.')
    #与 runCode统一格式
    res = {'data': r, 'msg': '', 'status': 1}
    return [json.dumps(res).encode('utf-8')]

if __name__ == '__main__':
    main()
